#ifndef __FEALIB_SIGNATURE__
#define __FEALIB_SIGNATURE__
namespace Fealib_signature
{
const unsigned int Mod_Prime_List_1[256]={
       256,     65536,        3,       768,    196608,         9,      2304,    589824,
        27,      6912,   1769472,        81,     20736,   5308416,       243,     62208,
  15925248,       729,    186624,  14221318,      2187,    559872,   9109528,      6561,
   1679616,  10551371,     19683,   5038848,  14876900,     59049,  15116544,  11076274,
    177147,  11795206,  16451609,    531441,   1831192,  15800401,   1594323,   5493576,
  13846777,   4782969,  16480728,   7985905,  14348907,  15887758,   7180502,   9492295,
  14108848,   4764293,  11699672,   8772118,  14292879,   1544590,   9539141,   9324211,
   4633770,  11840210,  11195420,  13901310,   1966204,     31834,   8149504,   5898612,
     95502,   7671299,    918623,    286506,   6236684,   2755869,    859518,   1932839,
   8267607,   2578554,   5798517,   8025608,   7735662,    618338,   7299611,   6429773,
   1855014,   5121620,   2512106,   5565042,  15364860,   7536318,  16695126,  12540154,
   5831741,  16530952,   4066036,    718010,  16038430,  12198108,   2154030,  14560864,
   3039898,   6462090,  10128166,   9119694,   2609057,  13607285,  10581869,   7827171,
   7267429,  14968394,   6704300,   5025074,  11350756,   3335687,  15075222,    497842,
  10007061,  11671240,   1493526,  13243970,   1459294,   4480578,   6177484,   4377882,
  13441734,   1755239,  13133646,   6770776,   5265717,   5846512,   3535115,  15797151,
    762323,  10605345,  13837027,   2286969,  15038822,   7956655,   6860907,  11562040,
   7092752,   3805508,   1131694,   4501043,  11416524,   3395082,  13503129,    695146,
  10185246,   6954961,   2085438,  13778525,   4087670,   6256314,   7781149,  12263010,
   1991729,   6566234,   3234604,   5975187,   2921489,   9703812,   1148348,   8764467,
  12334223,   3445044,   9516188,   3448243,  10335132,  11771351,  10344729,  14228183,
   1759627,  14256974,   9130123,   5278881,   9216496,  10613156,  15836643,  10872275,
  15062255,  13955503,  15839612,  11632339,   8312083,  13964410,   1342591,   8159036,
   8338804,   4027773,   7699895,   8239199,  12083319,   6322472,   7940384,   2695531,
   2190203,   7043939,   8086593,   6570609,   4354604,   7482566,   2934614,  13063812,
   5670485,   8803842,   5637010,    234242,   9634313,    133817,    702726,  12125726,
    401451,   2108178,   2822752,   1204353,   6324534,   8468256,   3613059,   2196389,
   8627555,  10839177,   6589167,   9105452,  15740318,   2990288,  10539143,  13666528,
   8970864,  14840216,   7445158,  10135379,  10966222,   5558261,  13628924,  16121453,
  16674783,   7332346,  14809933,  16469923,   5219825,  10875373,  15855343,  15659475,
  15848906,  14011603,  13423999,  13992292,   8480383,   6717571,   8422450,   8663936,
   3375500,   8490137,   9214595,  10126500,   8693198,  10866572,  13602287,   9302381
};

const unsigned int Mod_Prime_List_2[256]={
  15824477,   7761153,   7145686,    580925,  14499208,   4036269,   9875725,  11605750,
   1507777,    115335,  12748561,   8855010,   1960695,  15399149,  16317578,  16554616,
  10127548,   8963642,  12993288,   4396326,   1387123,   2782309,   7628746,   6803892,
  13744855,  12248289,  15002970,  15558948,   6894525,   3392505,  12844131,  16543731,
   7340988,    246640,  12808243,   7356403,   4192880,  16413743,   7618458,   4170164,
  10598447,  12073393,   3783992,  12401609,   3921293,  13996267,   9500965,  16330384,
   3055753,  10521614,   9181344,   1616204,  11095448,   5088057,  10698269,   4073427,
   2610974,  14098583,   2139463,  10832160,   4795125,   2816473,  16374730,  14408329,
  14325643,   9935226,  10060807,   8655145,   1126852,   3261729,  12919873,   2379285,
   5117796,   1534254,   6893447,   3116537,   9305119,  16525405,   2649532,   7192232,
  12496701,  11487646,   4827551,  11117529,  10740793,  14959571,   4448804,  14821491,
   2654722,   8520872,    307362,  11575876,  10637232,   5225154,  12240703,  13060954,
   4941623,   6765563,   3932631,    121596,  14351377,  16523130,   2067132,   9092623,
  12458026,   1586846,   3579800,  10460054,  10199183,  10525003,  10048928,   5614121,
  11153061,   3059786,  11554062,   5052848,   1684765,  11869865,   2012421,  11863806,
    461317,    656759,    358314,   7842389,  11164903,   6091338,  15880220,   5254162,
   2889552,   1528556,   5434759,  15567986,   9208253,   8504908,  12997777,   5545510,
  10365844,   2858622,  10387675,   8447358,  15042176,   8818485,   9387494,   4059007,
  15696653,   8592607,   1894323,  15185116,  11856727,  15426292,   6488987,    237971,
  10588979,   9649585,   4045507,  12240653,  13048154,   1664823,   6764713,   3715031,
  11524792,  14336927,  12823930,  11372275,   8846973,  16680422,   8779486,  16180949,
  15131990,  15033670,   6640949,   5585845,   3914405,  12232939,  11073370,  16213288,
   6633575,   3698101,   7190712,  12107581,  12536120,   4801711,   4502489,  11787652,
  14520291,   9433517,  15840895,  11964161,   9374998,    860031,   2064349,   8380175,
  14620527,   1539535,   8245383,  13668173,   9394896,   5953919,  14255354,   8718441,
    553429,   7460232,  13995905,   9408293,   9383551,   3049599,   8946190,   8525576,
   1511586,   1090439,  10717200,   8919763,   1760264,  14420410,    641180,  13147289,
  10266184,  10900060,   5400326,   6753138,    751831,   7919547,  14140152,  12781127,
    414707,   5501798,  15952771,   7050019,   9644571,   2761923,   2409930,  12962916,
  13398293,   7414412,   2265985,   9667394,   8604611,   4967347,  13350907,  12060795,
    558904,   8861832,   3707127,   9501368,  16433552,  12689562,  10528465,  10935200,
  14396166,  11211915,   1349211,   9854036,   6053366,   6159388,  16523821,   2244028
};

const unsigned int Mod_Prime_List_3[256]={
   4046946,  12610013,   6944192,  16108937,  13478037,  11054957,  11502248,   8568463,
  12492738,  10476158,  14324351,   9607962,  10169554,   2942459,  15073452,     51622,
  13215232,  10885609,   1703526,  16673081,   6904254,   5884809,  13341817,   9737003,
   9649684,   4073203,   2554622,  16450278,    198235,    416611,   5989318,   6541755,
  13748163,  13098481,  14551719,    705438,  12820298,  10445603,   6502271,   3640259,
   9161239,  13248747,   2688266,    331593,   1001893,   4826863,  10942569,  16285286,
   8291832,   8783934,    544582,   5195528,   4657711,   1194023,   3680594,   2709816,
   5848393,   4019321,   5538013,   8447956,  15197312,  14982599,  10347620,  14972989,
   7887460,   5927800,   7570330,   8628435,  11068387,  14940328,  16303427,  12935928,
   6492517,   1143235,   7456049,  12926865,   4172389,  11169055,   7156970,   3471373,
  16257972,   1299448,  13892211,  16420403,   9327418,   5459022,   5003443,   5815500,
  12375896,  14118972,   7362487,   5752176,  12942135,   8081509,   5272795,   7660880,
  15032052,   6230405,   1151295,   9519409,   4277169,   4438369,  12151203,   6929113,
  12248713,  15114490,  10557350,   1555137,  12239863,  12848890,    987972,   1263087,
   4583795,  15825893,   8127505,    270588,   2161796,  16549920,   8929404,   4230536,
   9277504,   9458221,   5390224,   4168338,  10131999,  10105562,   3337690,  15589490,
  14717069,   9480672,  11137680,  15902153,  10872882,  15222597,   4678376,   6484263,
  15807394,   3391761,  12654483,   1551329,  11265015,  14945547,    862308,   2647469,
   6664744,  11678981,   3480562,   1833173,  16308347,  14195448,  10163160,   1305595,
  15465843,  16617803,   9530269,   7057329,  11517643,  12509583,  14788478,  10984193,
  10163847,   1481467,  10157526,  16640474,  15334045,  16431881,  12265786,   2707995,
   5382217,   2118546,   5477920,   9841331,   2803286,  12999530,   5997446,   8622523,
   9554915,  13366705,  16108331,  13322901,   4894507,  11482250,   3448975,  10524084,
   9816224,  13153077,  11751112,   5168915,  14621966,   1911487,   2802365,  12763754,
  12747522,   8592130,   1774307,   1238651,  15105362,   8220582,   7321117,  11938639,
   2844278,   6716299,   8099878,   9975259,   3534488,  15638229,  10417070,  15975006,
  12746067,   8219650,   7082525,   1190636,   2813522,  15619946,   5736622,   8960311,
  12142728,   4759513,  10478152,  14834815,   6069282,  10235356,   3010588,  15737293,
   2223088,  15463489,  16015179,   6253172,   6979647,   8408234,   5028480,  12224972,
   9036794,  14945193,    771684,  13002091,   6653062,   8688389,   9639428,   1447667,
   1504726,  16111830,  14218645,  16101592,  11597717,  16231344,  11259863,  13626635,
  15541679,   2477453,  13472197,   9559917,  14647217,   8375743,  13487967,  13597037
};

const int AF_MOD = 255;

#define MUL_ADD2(a,b,prime,idx) (a*prime[idx&AF_MOD]+b*prime[(idx+1)&AF_MOD])

#define MUL_ADD3(a,b,c,prime,idx) (a*prime[idx&AF_MOD]+b*prime[(idx+1)&AF_MOD]+c*prime[(idx+2)&AF_MOD])

#define MUL_ADD4(a,b,c,d,prime,idx) (a*prime[idx&AF_MOD]+b*prime[(idx+1)&AF_MOD]+\
									c*prime[(idx+2)&AF_MOD]+d*prime[(idx+3)&AF_MOD])

#define MUL_ADD5(a,b,c,d,e,prime,idx) (a*prime[idx&AF_MOD]+b*prime[(idx+1)&AF_MOD]+\
										c*prime[(idx+2)&AF_MOD]+d*prime[(idx+3)&AF_MOD]+e*prime[(idx+4)&AF_MOD])

#define STR_SIG(str,sig,prime,idx) for(int i=0;str[i];i++)sig+=str[i]*prime[(idx+i)&AF_MOD];

#define STR_SIG_INC(str,sig,prime,idx) for(int i=0;str[i];i++)sig+=str[i]*prime[(idx++)&AF_MOD];

#define SIGN64_SIG_INC(data, sig, prime, idx) sig+=((uint32_t)(data&AF_MASK))*prime[(idx++)&AF_MOD] + ((uint32_t)(data>>32))*prime[(idx++)&AF_MOD];

#define SIGN32_SIG_INC(data, sig, prime, idx) sig+=(data*prime[(idx++)&AF_MOD]);

const unsigned int AF_MASK  = 0xffffffffU;

#define MUL_ADDX()

/*
Note that two char*s must be separated by a '-'
*/
// Function arguments order:  uint32_t < bool < const char*
//Begin with uint32
inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint32_t data1)
{
	sig1=type;
	sig2=data1;
}
inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint32_t data1,uint32_t data2)
{
	sig1=MUL_ADD3(type,data1,data2,Mod_Prime_List_1,0);
	sig2=MUL_ADD3(type,data1,data2,Mod_Prime_List_2,0);
}
inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint32_t data1,uint64_t data2)
{
	sig1=MUL_ADD4(type,data1,(uint32_t)(data2&AF_MASK),(uint32_t)(data2>>32),Mod_Prime_List_1,0);
	sig2=MUL_ADD4(type,data1,(uint32_t)(data2&AF_MASK),(uint32_t)(data2>>32),Mod_Prime_List_2,0);
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint32_t data1,bool data2)
{
	sig1=MUL_ADD2(type,data1,Mod_Prime_List_1,0);
	sig2=MUL_ADD2(type,data1,Mod_Prime_List_2,0);
	
	sig1+=data2?Mod_Prime_List_1[2]:0;
	sig2+=data2?Mod_Prime_List_2[2]:0;
}
//Begin with uint64_t

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint64_t data1)
{
	sig1=MUL_ADD3(type,(uint32_t)(data1&AF_MASK),(uint32_t)(data1>>32),Mod_Prime_List_1,0);
	sig2=MUL_ADD3(type,(uint32_t)(data1&AF_MASK),(uint32_t)(data1>>32),Mod_Prime_List_2,0);
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint64_t data1,uint64_t data2)
{
	sig1=MUL_ADD5(type,(uint32_t)(data1&AF_MASK),(uint32_t)(data1>>32),(uint32_t)(data2&AF_MASK),
							(uint32_t)(data2>>32),Mod_Prime_List_1,0);
	sig2=MUL_ADD5(type,(uint32_t)(data1&AF_MASK),(uint32_t)(data1>>32),(uint32_t)(data2&AF_MASK),
							(uint32_t)(data2>>32),Mod_Prime_List_2,0);
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint64_t data[], uint32_t length)
{
	sig1=MUL_ADD3(type,(uint32_t)(data[0]&AF_MASK),(uint32_t)(data[0]>>32),Mod_Prime_List_1,0);
	sig2=MUL_ADD3(type,(uint32_t)(data[0]&AF_MASK),(uint32_t)(data[0]>>32),Mod_Prime_List_2,0);
	uint32_t idx1=3,idx2=3;
	for(uint32_t i=1;i<length;i++){
		SIGN64_SIG_INC(data[i], sig1, Mod_Prime_List_1, idx1);
		SIGN64_SIG_INC(data[i], sig2, Mod_Prime_List_2, idx2);
	}
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint32_t data[], uint32_t length)
{
	sig1=type*Mod_Prime_List_1[0];
	sig2=type*Mod_Prime_List_2[0];
	uint32_t idx1=1,idx2=1;
	for(uint32_t i=0;i<length;i++){
		SIGN32_SIG_INC(data[i], sig1, Mod_Prime_List_1, idx1);
		SIGN32_SIG_INC(data[i], sig2, Mod_Prime_List_2, idx2);
	}
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint64_t data1,char* str1)
{
	sig1=MUL_ADD3(type,(uint32_t)(data1&AF_MASK),(uint32_t)(data1>>32),Mod_Prime_List_1,0);
	sig2=MUL_ADD3(type,(uint32_t)(data1&AF_MASK),(uint32_t)(data1>>32),Mod_Prime_List_2,0);
	STR_SIG(str1,sig1,Mod_Prime_List_1,3);
	STR_SIG(str1,sig2,Mod_Prime_List_2,3);
}


//Begin with bool
inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,bool data)
{
	sig1=MUL_ADD2(type,data?1:0,Mod_Prime_List_1,0);
	sig2=MUL_ADD2(type,data?1:0,Mod_Prime_List_2,0);
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,bool data1,bool data2)
{
	sig1=MUL_ADD3(type,data1?1:0,data2?1:0,Mod_Prime_List_1,0);
	sig2=MUL_ADD3(type,data1?1:0,data2?1:0,Mod_Prime_List_2,0);
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,bool data,const char* str1)
{
	
	sig1=MUL_ADD2(type,data?1:0,Mod_Prime_List_1,0);
	sig2=MUL_ADD2(type,data?1:0,Mod_Prime_List_2,0);
	
	STR_SIG(str1,sig1,Mod_Prime_List_1,1);
	STR_SIG(str1,sig2,Mod_Prime_List_2,1);
}

inline void create_sign64_beg(uint32_t& sig1,uint32_t& sig2,const char* str)
{
	sig1 = 0;
	sig2 = 0;
	STR_SIG(str,sig1,Mod_Prime_List_1,1);
	STR_SIG(str,sig2,Mod_Prime_List_2,1);
}
//Begin with const char*
inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,const char* str1)
{
	sig1=type*Mod_Prime_List_1[0];
	sig2=type*Mod_Prime_List_2[0];
	STR_SIG(str1,sig1,Mod_Prime_List_1,1);
	STR_SIG(str1,sig2,Mod_Prime_List_2,1);
}

inline void create_sign64_end(uint32_t& sig1,uint32_t& sig2, uint32_t type, uint32_t &str_sign1, uint32_t &str_sign2)
{
	sig1=type*Mod_Prime_List_1[0];
	sig2=type*Mod_Prime_List_2[0];
	sig1 += str_sign1;
	sig2 += str_sign2;
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,uint32_t data1,const char* str2)
{
	sig1=MUL_ADD2(type,data1,Mod_Prime_List_1,0);
	sig2=MUL_ADD2(type,data1,Mod_Prime_List_2,0);
	STR_SIG(str2,sig1,Mod_Prime_List_1,2);
	STR_SIG(str2,sig2,Mod_Prime_List_2,2);	
}

inline void create_sign64(uint32_t& sig1,uint32_t& sig2,uint32_t type,const char* str1,const char* str2)
{
	sig1=type*Mod_Prime_List_1[0];
	sig2=type*Mod_Prime_List_2[0];
	int idx1=1,idx2=1;
	STR_SIG_INC(str1,sig1,Mod_Prime_List_1,idx1);
	STR_SIG_INC(str1,sig2,Mod_Prime_List_2,idx2);
	
	sig1+='-'*Mod_Prime_List_1[(idx1++)&AF_MASK];
	sig2+='-'*Mod_Prime_List_2[(idx2++)&AF_MASK];
	
	STR_SIG_INC(str2,sig1,Mod_Prime_List_1,idx1);
	STR_SIG_INC(str2,sig2,Mod_Prime_List_2,idx2);
}

};
#endif
